//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Text.RegularExpressions;
using CommunityServer.Components.Enumerations;
using CommunityServer.Configuration;

namespace CommunityServer.Components 
{
	public enum SectionSortBy 
	{
		SortOrder,
		Name,
		Thread,
		Post,
		LastPost
	}

	/// <summary>
	/// A Section is the base container for a group of posts. 
	/// </summary>
	[Serializable]
	public abstract class Section : ExtendedAttributes
	{

		#region Private Members
		// Member Variables
		int sectionID = 0;				// Unique section identifier
		int parentId = 0;
		int settingsID = 0;
		// moved to constructor try/catch for import/export
		ThreadDateFilterMode threadDateFilter; //= CSContext.Current.SiteSettings.DefaultThreadDateFilter;
		int totalPosts = -1;			// Total posts in the forum
		int totalThreads = -1;			// Total threads in the forum
		int groupId = -1;          // Identifier for the forum group this forum belongs to
		int sortOrder = 0;              // Used to control sorting of forums
		String name = "";				// Name of the forum
		String description = "";		// Description of the forum
		bool isModerated = false;	    // Is the forum isModerated?
		bool isActive = true;          // Is the forum isActive?
		bool isVisible = false;		// Used for display filtering
		DateTime mostRecentPostDate = DateTime.MinValue.AddMonths(1);	        // The date of the most recent post to the forum
		int mostRecentPostId = 0;       // the most recent post id
		int mostRecentThreadId = 0;     // Post ID of the most recent thread
		String mostRecentUser = "";		// The author of the most recent post to the forum
		string mostRecentPostSubject =""; // most recent post subject
		DateTime dateCreated;			// The date/time the forum was created
		byte[] displayMask;
		Hashtable permissions = new Hashtable();
		//        ForumPermission permission = new ForumPermission();
		static SectionSortBy sortBy = SectionSortBy.SortOrder;
		ArrayList sections = null;
		bool enablePostStatistics = true;
		bool enablePostPoints = true;
		bool enableAutoDelete = false;
		bool enableAnonymousPosts = false;
		bool isSearchable = true;
		int autoDeleteThreshold = 90;
		int mostRecentAuthorID;
		int postsToModerate = 0;
		int mostRecentThreadReplies = 0;
		Int64 diskUsage = 0;
		string defaultLanguage = null;

		private string appKey;
		private ApplicationType appType = ApplicationType.Unknown;
		#endregion

		public Section() 
		{
			try 
			{
				threadDateFilter = CSContext.Current.SiteSettings.DefaultThreadDateFilter;
			} 
			catch 
			{
				threadDateFilter = ThreadDateFilterMode.TwoMonths;
			}
		}
		public Section(int sectionID) : this() { SectionID = sectionID; }


		public override object Copy()
		{
			Section s =  base.Copy () as Section;
			s.ApplicationKey = this.ApplicationKey;
			s.ApplicationType = this.ApplicationType;
			s.AutoDeleteThreshold = this.AutoDeleteThreshold;
			s.DateCreated = this.DateCreated;
			s.DefaultThreadDateFilter = this.DefaultThreadDateFilter;
			s.Description = this.Description;
			s.DisplayMask = this.DisplayMask;
			s.ForumType = this.ForumType;
			s.NavigateUrl = this.NavigateUrl;
			s.NewsgroupName = this.NewsgroupName;
			s.PostsToModerate = this.PostsToModerate;
			s.EnablePostStatistics = this.EnablePostStatistics;
			s.EnablePostPoints = this.EnablePostPoints;
			s.EnableAnonymousPosting = this.EnableAnonymousPosting;
			s.Sections = this.Sections;
			s.SectionID = this.SectionID;
			s.SortOrder = this.SortOrder;
			s.GroupID = this.GroupID;
			s.IsActive = this.IsActive;
			s.IsVisible = this.IsVisible;
			s.IsCommunityAggregated = this.IsCommunityAggregated;
			s.MostRecentPostAuthorID = this.MostRecentPostAuthorID;
			s.MostRecentPostAuthor = this.MostRecentPostAuthor;
			s.MostRecentPostDate = this.MostRecentPostDate;
			s.MostRecentPostID = this.MostRecentPostID;
			s.MostRecentPostSubject = this.MostRecentPostSubject;
			s.MostRecentThreadID = this.MostRecentThreadID;
			s.MostRecentThreadReplies = this.MostRecentThreadReplies;
			s.Name = this.Name;
			s.ParentID = this.ParentID;
			s.IsSearchable = this.IsSearchable;
			s.IsModerated = this.IsModerated;
			s.PermissionSet = new Hashtable(this.PermissionSet);
			s.SettingsID = this.SettingsID;
			s.TotalPosts = this.TotalPosts;
			s.TotalThreads = this.TotalThreads;
			s.DiskUsage = this.DiskUsage;
			s.DefaultLanguage = this.DefaultLanguage;


			return s;
		}


		#region Public Properties
		public string ApplicationKey
		{
			get{return appKey;}
			set
			{
				if(value != null && value.Trim().Length > 0)
					appKey = value.ToLower().Replace(" ","_");
				else
					appKey = null;
			}
		}

		public ApplicationType ApplicationType
		{
			get{return appType;}
			set{appType = value;}
		}

		ForumType forumType = ForumType.Normal;
		public ForumType ForumType 
		{
			get 
			{
				return forumType;
			}
			set 
			{
				forumType = value;
			}
		}

		string navigateUrl;
		public string NavigateUrl 
		{
			get 
			{
				return navigateUrl;
			}
			set 
			{
				navigateUrl = value;
			}
		}

		public int AutoDeleteThreshold 
		{
			get 
			{
				return autoDeleteThreshold;
			}
			set 
			{
				autoDeleteThreshold = value;
			}
		}

		string newsgroupName;
		public string NewsgroupName 
		{
			get 
			{ 
				if (newsgroupName != string.Empty)
					return newsgroupName.ToLower();

				return name.Replace(" ", ".").ToLower();
			}
			set { newsgroupName = value; }
             
		}

		public int PostsToModerate 
		{
			get 
			{
				return postsToModerate;
			}
			set 
			{
				postsToModerate = value;
			}
		}
        
		public bool EnablePostStatistics 
		{
			get 
			{
				return enablePostStatistics;
			}
			set 
			{
				enablePostStatistics = value;
			}
		}

		public bool EnablePostPoints
		{
			get 
			{
				return enablePostPoints;
			}
			set 
			{
				enablePostPoints = value;
			}
		}

		public bool EnableAnonymousPosting 
		{
			get 
			{
				return enableAnonymousPosts;
			}
			set 
			{
				enableAnonymousPosts = value;
			}
		}

		public bool IsSearchable 
		{
			get 
			{
				return isSearchable;
			}
			set 
			{
				isSearchable = value;
			}
		}

		public bool EnableAutoDelete 
		{
			get 
			{
				return enableAutoDelete;
			}
			set 
			{
				enableAutoDelete = value;
			}
		}

		public ArrayList Sections 
		{
			get
			{
				if(sections == null)
					sections = new ArrayList();

				return sections;
			}
			set { sections = value; }
		}

		public Int64 DiskUsage
		{
			get
			{
				return diskUsage;
			}
			set
			{
				diskUsage = value;
			}
		}


		public string DefaultLanguage
		{
			get
			{
				if (defaultLanguage == null)
				{
					FilterLanguage fl = CSConfiguration.GetConfig().DefaultFilterLanguage;
					return fl == null ? null : fl.Key;
				}
				else
					return defaultLanguage;
			}
			set
			{
				defaultLanguage = value;
			}
		}

		public string MetaTagDescription 
		{
			get{return GetExtendedAttribute("metaTagDescription");}
			set{SetExtendedAttribute("metaTagDescription", value);}
		}

		public string MetaTagKeywords 
		{
			get{return GetExtendedAttribute("metaTagKeywords");}
			set{SetExtendedAttribute("metaTagKeywords", value);}
		}

		public static SectionSortBy SortBy 
		{
			get 
			{
				return sortBy;
			}
			set 
			{
				sortBy = value;
			}
		}

		// *********************************************************************
		//  IsAnnouncement
		//
		/// <summary>
		/// If post is locked and post date > 2 years
		/// </summary>
		// ********************************************************************/
		public virtual bool IsAnnouncement 
		{
			get 
			{ 
				if (MostRecentPostDate > DateTime.Now.AddYears(2))
					return true;
				else
					return false;
			}
		}

		// *********************************************************************
		//  IsPrivate
		//
		/// <summary>
		/// Is the forum private, e.g. a role is required to access?
		/// </summary>
		// ********************************************************************/
		public virtual bool IsPrivate 
		{
			get { return !Permissions.ValidatePermissions(this,Permission.View,Users.GetAnonymousUser(true)); }
		}

		/*************************** PROPERTY STATEMENTS *****************************/
		/// <summary>
		/// Specifies the unique identifier for the each forum.
		/// </summary>
		public int SectionID 
		{
			get { return sectionID; }
			set 
			{
				if (value < 0)
					sectionID = 0;
				else
					sectionID = value;
			}
		}

        
		// *********************************************************************
		//  ParentID
		//
		/// <summary>
		/// If ParentId > 0 this forum has a parent and is not a top-level forum
		/// </summary>
		// ********************************************************************/
		public int ParentID 
		{
			get { return parentId; }
			set 
			{
				if (value < 0)
					parentId = 0;
				else
					parentId = value;
			}
		}


		// *********************************************************************
		//  SettingsID
		//
		/// <summary>
		/// Specifies the unique identifier for the site the forum belongs to.
		/// </summary>
		// ********************************************************************/
		public int SettingsID 
		{
			get { return settingsID; }
			set 
			{
				if (value < 0)
					settingsID = 0;
				else
					settingsID = value;
			}
		}

        
		// *********************************************************************
		//  DisplayMask
		//
		/// <summary>
		/// Bit mask used to control what forums to display for the current user
		/// </summary>
		// ********************************************************************/
		public byte[] DisplayMask 
		{
			get 
			{ 
				return displayMask; 
			}
			set 
			{
				displayMask = value;
			}
		}

               
		public int GroupID 
		{
			get { return groupId; }
			set 
			{
				if (value < 0)
					groupId = 0;
				else
					groupId = value;
			}
		}

		public int SortOrder 
		{
			get { return sortOrder; }
			set { sortOrder = value; }
		}

		/// <summary>
		/// Indicates how many total posts the forum has received.
		/// </summary>
		public int TotalPosts 
		{
			get { return totalPosts; }
			set 
			{
				if (value < 0)
					totalPosts = -1;
				else
					totalPosts = value;
			}
		}


		/// <summary>
		/// Specifies the date/time of the most recent post to the forum.
		/// </summary>
		public DateTime MostRecentPostDate 
		{
			get { return mostRecentPostDate; }
			set 
			{
				mostRecentPostDate = value;
			}
		}

		/// <summary>
		/// Specifies the most recent post to the forum.
		/// </summary>
		public int MostRecentPostID 
		{
			get { return mostRecentPostId; }
			set 
			{
				mostRecentPostId = value;
			}
		}

		/// <summary>
		/// Specifies the most recent thread id to the forum.
		/// </summary>
		public int MostRecentThreadID 
		{
			get { return mostRecentThreadId; }
			set 
			{
				mostRecentThreadId = value;
			}
		}

		public int MostRecentThreadReplies 
		{
			get { return mostRecentThreadReplies ; }
			set { mostRecentThreadReplies  = value; }

		}

		/// <summary>
		/// Specifies the author of the most recent post to the forum.
		/// </summary>
		public String MostRecentPostAuthor 
		{
			get { return mostRecentUser; }
			set 
			{
				mostRecentUser = value;
			}
		}

		/// <summary>
		/// Specifies the author of the most recent post to the forum.
		/// </summary>
		public String MostRecentPostSubject 
		{
			get { return mostRecentPostSubject; }
			set 
			{
				mostRecentPostSubject = value;
			}
		}

		/// <summary>
		/// Specifies the author of the most recent post to the forum.
		/// </summary>
		public int MostRecentPostAuthorID 
		{
			get { return mostRecentAuthorID; }
			set 
			{
				mostRecentAuthorID = value;
			}
		}

		/// <summary>
		/// Indicates how many total threads are in the forum.  A thread is a top-level post.
		/// </summary>
		public int TotalThreads 
		{
			get { return totalThreads; }
			set 
			{
				if (value < 0)
					totalThreads = -1;
				else
					totalThreads = value;
			}
		}

		/// <summary>
		/// Specifies how many days worth of posts to view per page when listing a forum's posts.
		/// </summary>
		public ThreadDateFilterMode DefaultThreadDateFilter 
		{
			get { return threadDateFilter; }
			set { threadDateFilter = value; }
		}

		/// <summary>
		/// Specifies the name of the forum.
		/// </summary>
		public String Name 
		{
			get { return name; }
			set { name = value; }
		}

		/// <summary>
		/// Specifies the description of the forum.
		/// </summary>
		public String Description 
		{
			get { return description; }
			set { description = value; }
		}

		/// <summary>
		/// Specifies if the forum is isModerated or not.
		/// </summary>
		public bool IsModerated 
		{
			get { return isModerated; }
			set { isModerated = value; }
		}

		/// <summary>
		/// Specifies if the forum is currently isActive or not.  InisActive forums do not appear in the
		/// ForumListView Web control listing.
		/// </summary>
		public bool IsActive 
		{
			get { return isActive; }
			set { isActive = value; }
		}

		/// <summary>
		/// Specifies if the section is displayed when rendered. This is a property set per-user
		/// </summary>
		public bool IsVisible 
		{
			get { return isVisible; }
			set { isVisible = value; }
		}


		/// <summary>
		/// Returns the date/time the forum was created.
		/// </summary>
		public DateTime DateCreated 
		{
			get { return dateCreated; }
			set { dateCreated = value; }
		}


		#region Spam

		/// <summary>
		/// Score above which a post is auto-moderated by Spam Blocker
		/// Overrides site setting
		/// </summary>
		public int SpamAutoModerateScore
		{
			get {  return GetInt("SpamAutoModerateScore",-1); }
			set { SetExtendedAttribute("SpamAutoModerateScore", value.ToString()); }
		}

		/// <summary>
		/// Score above which a post is auto-deleted by Spam Blocker
		/// Overrides site setting
		/// </summary>
		public int SpamAutoDeleteScore
		{
			get {  return GetInt("SpamAutoDeleteScore",-1); }
			set { SetExtendedAttribute("SpamAutoDeleteScore", value.ToString()); }
		}

		#endregion

		#endregion


        
		/// <summary>
		/// Property Owners (string)
		/// </summary>
		public string Owners
		{
			get {  return GetExtendedAttribute("SectionOwners"); }
			set
			{
				if(value != null && value.Trim().Length > 0)
				{
					// trim each entry
					string[] owners = value.Split(';');
					for (int i = 0; i < owners.Length; i++)
						owners[i] = owners[i].Trim();

					SetExtendedAttribute("SectionOwners", string.Join(";", owners));
				}
				else
					SetExtendedAttribute("SectionOwners", null);

				ownerArray = null;
			}
		}

		string[] ownerArray = null;
		public string[] OwnerArray
		{
			get
			{
				if(ownerArray == null)
					ownerArray = Owners.Split(';');

				return ownerArray;
			}
		}

        public bool IsOwner(string userName)
        {
            bool isOwner = false;
            foreach (string owner in OwnerArray)
                if (owner.Trim().ToLower().Equals(userName.ToLower()))
                    isOwner = true;
            return isOwner;
        }


		/****************************************************************************/
		/// <summary>
		/// Property to access the collection of ForumPermissions applied to this forum. Use RoleName as the lookup
		/// </summary>
		public Hashtable PermissionSet 
		{
			get { return this.permissions; }
			set { permissions = value; }
		}

		public bool HasPermissions
		{
			get{ return (this.PermissionSet != null && this.PermissionSet.Count > 0);}
		}

		/// <summary>
		/// Each section type must return it's permission type
		/// </summary>
		public abstract PermissionBase DefaultRolePermission
		{
			get;
		}

		/// <summary>
		/// Provides the Method implementing the AccessCheckDelegate signature
		/// </summary>
		public abstract AccessCheckDelegate AccessCheck
		{
			get;
		}

		public abstract string Url { get;}

		/// <summary>
		/// Provides the Method implementing the ValidateDelegate signature
		/// </summary>
		public abstract ValidatePermissionsDelegate ValidatePermissions
		{
			get;
		}

		public virtual PermissionBase OwnerPermission
		{
			get
			{
				return PermissionSet["Owners"] as PermissionBase;
			}
		}

		public string[] AllowedRoles(Permission p)
		{
			ArrayList list = new ArrayList();
			foreach(string role in PermissionSet.Keys)
			{
				if((((PermissionBase)PermissionSet[role]).AllowMask & p) != Permission.Undefined)
					list.Add(role);
			}

			return (string[])list.ToArray(typeof(string));

		}

		public virtual PermissionBase RolePermission( string roleName )
		{
			PermissionBase pb = PermissionSet[roleName] as PermissionBase;
			if(pb == null)
				pb = DefaultRolePermission;

			return pb;
		}


		/// <summary>
		/// Validates if a user the owner of the current section. Owners are users who have
		/// elevated permissions but no specific roles assigned. If the user is the owner, 
		/// their role based security will be ignored
		/// </summary>
		/// <param name="user">User to evaluate</param>
		/// <returns>True if the user's UserName exists as part of the Owners property (split into an array)</returns>
		protected virtual bool IsOwner(User user)
		{
			if(OwnerArray != null && OwnerArray[0].Length > 0)
			{
				foreach(string owner in OwnerArray)
				{
					if(string.Compare(owner,user.Username,true) == 0)
						return true;
				}
			}

			return false;
		}

        

		/// <summary>
		/// A user can be in multiple roles. Resolved Permission Merges the permissions
		/// for the user and those valid for the current section
		/// </summary>
		/// <param name="user">User we are evaluating</param>
		/// <returns>A single unified permission for the section/user combination</returns>
		public PermissionBase ResolvePermission( User user ) 
		{
			if(IsOwner(user))
				return OwnerPermission;

			PermissionBase pbMaster = DefaultRolePermission;

			string[] roles = Roles.GetUserRoleNames(user.Username);
			PermissionBase pb = null;
			foreach(string role in roles)
			{
				pb = PermissionSet[role] as PermissionBase;  
				if(pb !=null)
					pbMaster.Merge(pb);
			}

			return pbMaster;
		}		
        
		/// <summary>
		/// Specifies if autheticated users are able to post as anonymous.
		/// </summary>
		public bool EnableAnonymousPostingForUsers 
		{
			get 
			{
				try 
				{
					return bool.Parse( GetExtendedAttribute( "EnableAnonymousPostingForUsers" ) ); 
				} 
				catch 
				{
					return false;
				}
			}
			set 
			{
				SetExtendedAttribute( "EnableAnonymousPostingForUsers", value.ToString() );
			}
		}

		/// <summary>
		/// Controls whether thread starters may set at the forum level the status 
		/// of their thread with Not Set, Answered or Not Answered.
		/// </summary>
		public bool EnableThreadStatus 
		{
			get 
			{
				try 
				{
					return bool.Parse( GetExtendedAttribute( "EnableThreadStatus" ) ); 
				} 
				catch 
				{
					return false;
				}
			}
			set 
			{
				SetExtendedAttribute( "EnableThreadStatus", value.ToString() );
			}
		}

		/// <summary>
		/// Lazy loaded property which looks up the group name for the current group id and application type.
		/// </summary>
		public string GroupName
		{
			get 
			{ 
			
				Hashtable groupsTable = CommonDataProvider.Instance().GetGroups(this.ApplicationType, false);
				foreach(Group fg in groupsTable.Values)
				{
					if(fg.GroupID == this.GroupID)
						return fg.Name;
				}
				return "";
			}
		}

		public PostCategorySortBy CategorySortBy
		{
			get
			{
				string val = GetExtendedAttribute( "CategorySortBy" );
				if(val != string.Empty)
					return (PostCategorySortBy)Enum.Parse(typeof(PostCategorySortBy), val);
				else
					return PostCategorySortBy.Name;
			}
			set
			{
				SetExtendedAttribute( "CategorySortBy", value.ToString() );
			}
		}


		/// <summary>
		/// Is the section included in the root aggregate views
		/// </summary>
		public bool IsCommunityAggregated
		{
			get {  return GetBool("IsCommunityAggregated", true); }
			set{ SetExtendedAttribute("IsCommunityAggregated", value.ToString()); }
		}


		/// <summary>
		/// Determines whether to block feedback flagged as SPAM from being emailed to blog owners and email subscribers.
		/// </summary>
		public bool BlockSpamFeedbackNotifications
		{
			get { return GetBool("BlockSpamFeedbackNotifications", false); }
			set { SetExtendedAttribute("BlockSpamFeedbackNotifications", value.ToString()); }
		}

		//        /// <summary>
		//        /// Determines of Sections which do not have allow anonymous users to view them should allow 
		//        /// Syndication using the PublicToken
		//        /// </summary>
		//        public bool EnableSecureSyndication
		//        {
		//            get { return GetBool("EnableSecureSyndication",false);}
		//            set { SetExtendedAttribute("EnableSecureSyndication",value.ToString());}
		//        }

	}
}
